package gen.tools.chkin;

import gen.tools.CommitMediator;
import gen.tools.JobDepStruc;
import gen.tools.Widgets;
import gen.tools.structures.B1I1;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import mailGenerator.MailGenerator;

import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.io.ISVNEditor;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

public class ApproveChkIn extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    public ApproveChkIn() {
        super();
    }

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doPost(request,response);
	}

	@SuppressWarnings("resource")
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		

		final Widgets wid = new Widgets();
		request.setCharacterEncoding( wid.UIcodeSet );
		response.setCharacterEncoding( wid.UIcodeSet );
		response.setContentType("application/text");
		
		
		/**
		 * 檢查這筆資料是不是還在(沒有其它主管先回覆了) 
		 * 將檔案從 SVN AP 寫入 SVN CM OL
		 * 計算此模組所推導的最後部署模組有那些，還有中間經過的點，以及上述各點的預期Build版的job版數
		 * 取得Build版封裝的進度以寫入DB
		 * 改變繳交的狀態
		 * 刪除todolist
		 * 寄發通知信(開發人員、CM人員、測試人員)
		 **/
		
		final boolean isApprove = Integer.parseInt(request.getParameter("isApprove"))==1?true:false;	//主管是否同意
		final int chkInId = Integer.parseInt(request.getParameter("chkInId"));	//繳交申請ID
		final int toDoListId = wid.getParNumberFromRequest( request.getParameter("toDoListId") );
		final String subsysId = request.getParameter("subsysId");
		final String applierId = request.getParameter("creatorId");
		final String empId = request.getParameter("empId");
		final int changeTypeId = (isApprove?3:2);
		
		int chkInStatusId = isApprove?3:2;	//繳交的狀態，若主管同意則改為3(簽入SVN中)，不同意則是2(審核拒絕)
		System.out.println("繳交申請ID："+chkInId);
		
		String sql;
		String res = null;
		Connection conn = null;
		PreparedStatement ps = null;
		PreparedStatement ps2 = null;
		ResultSet rs = null;
		ResultSet rs2 = null;
		
		//SVN抓取檔案要用
		InputStream input = null;
		ByteArrayOutputStream baos = null;

		SVNRepository cmOLrepo = null;	//SVN CM OL的Repo
		SVNRepository cmOLrepoCHK = null;	//SVN CM OL的Repo	為了做檢查用
		SVNRepository apMRrepo = null;	//SVN AP MR的Repo
		SVNRepository apOLrepo = null;	//SVN AP OL的Repo
		SVNRepository apRepo = null;	//SVN AP 的repo
		
		try {
			
			conn = wid.getConn();
			
			/****檢查有沒有其它主管先回覆了****/
			sql="SELECT chkInStatusId FROM chkinmain WHERE chkInMainId=?";
			ps = conn.prepareStatement(sql);
			ps.setInt(1, chkInId);
			rs = ps.executeQuery();
			rs.next();
			if ( rs.getInt(1)==1 ) {	//表示仍處於待審的狀態
				
				/****建立LOG，第一步就開始記錄程式的執行****/
				sql="INSERT INTO changelog (changeLogId,changeTypeId,creatorId,bDate) VALUES (null,"+changeTypeId+",?,?)";
				ps = conn.prepareStatement(sql);
				ps.setString(1, empId);
				ps.setTimestamp(2, wid.cDT());
				ps.execute();
				sql="SELECT MAX(changeLogId) FROM changelog";
				ps = conn.prepareStatement(sql);
				rs = ps.executeQuery();
				rs.next();
				final int chkInId4Approve = rs.getInt(1);
				
				//修改繳交記錄的狀態
				wid.updateCHKINstatus(chkInId, chkInStatusId);
				
				//取得提出者的寄件地址
				final String mailList[] = wid.getEmailAddr(applierId);
				
				
				
				//刪除已完成的待辦事項，將其加入待辦清單
				wid.completedList(toDoListId, applierId);
		
				final MailGenerator mg = new MailGenerator();
				final String mailSubject = mg.subjectMail(isApprove?3:4);
				final String mailBody = mg.mailBodyMail(isApprove?3:4,wid.getEmpName(empId));
				
				if ( isApprove ) {
					
					//將檔案從 SVN AP 寫入 SVN CM OL
					try {
						ISVNEditor editor = null;
						SVNCommitInfo info = null;
						final SVNProperties fileProperties = new SVNProperties();
						baos = new ByteArrayOutputStream();
						
						/*	此區開始建立SVN連線	*/
						final ArrayList<String> connCMsvnOLinfo = wid.getSVN4SubsysConnInfo(subsysId, 2);	//SVN CM OL的連線資訊
						final ArrayList<String> connAPsvnMRinfo = wid.getSVN4SubsysConnInfo(subsysId, 3);	//SVN AP MR的連線資訊
						final ArrayList<String> connAPsvnOLinfo = wid.getSVN4SubsysConnInfo(subsysId, 4);	//SVN AP OL的連線資訊
						
						ISVNAuthenticationManager authManager = null;
						apMRrepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(connAPsvnMRinfo.get(0)));	//連線到SVN AP MR 的repo
						apOLrepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(connAPsvnOLinfo.get(0)));	//連線到SVN AP OL 的repo
				        authManager = SVNWCUtil.createDefaultAuthenticationManager(connAPsvnMRinfo.get(1), connAPsvnMRinfo.get(2));
				        apMRrepo.setAuthenticationManager(authManager);
				        authManager = SVNWCUtil.createDefaultAuthenticationManager(connAPsvnOLinfo.get(1), connAPsvnOLinfo.get(2));
				        apOLrepo.setAuthenticationManager(authManager);
				        
				        final SVNDeltaGenerator deltaGenerator = new SVNDeltaGenerator();
						
				        //開始查詢有那些要處理的檔案
				        sql = "SELECT modId FROM chkinmod WHERE chkInMainId=?";
			            ps = conn.prepareStatement(sql);
			            ps.setInt(1, chkInId);
			            rs = ps.executeQuery();
			            cmOLrepo = SVNRepositoryFactory.create(SVNURL.parseURIEncoded( connCMsvnOLinfo.get(0) ));	//連線到SVN CM OL 的repo
			        	cmOLrepoCHK = SVNRepositoryFactory.create(SVNURL.parseURIEncoded( connCMsvnOLinfo.get(0) ));	//連線到SVN CM OL 的repo
			        	authManager = SVNWCUtil.createDefaultAuthenticationManager(connCMsvnOLinfo.get(1), connCMsvnOLinfo.get(2));
			    		cmOLrepo.setAuthenticationManager(authManager);
			    		cmOLrepoCHK.setAuthenticationManager(authManager);
			    		
			            while ( rs.next() ) {	//此while loop即要處理的模組
			            	editor = cmOLrepo.getCommitEditor(null, new CommitMediator() );
			            	editor.openRoot(-1);
			            	final String modId = rs.getString("modId");
			            	sql = "SELECT isMR,fileName,relativePath,fileVer FROM chkinmoddetail WHERE chkInMainId=? AND modId=?";
			            	ps2 = conn.prepareStatement(sql);
			    	        ps2.setInt(1, chkInId);
			    	        ps2.setString(2, modId);
			    	        rs2 = ps2.executeQuery();
			    	        try {	//若能夠打開該路徑的資料夾，便是該夾存在，否則便進入catch來創建該資料夾
		                		System.out.println("嘗試新增模組夾"+modId);
		                		editor.addDir(modId,null,-1);
		                	} catch ( Exception e) {
		                		System.out.println("無法新增，嘗試打開模組夾："+modId);
		                		//editor.openDir(modId,-1);	//不能OPEN程式才會正確跑!!
		                	}
			    	        
			    	        while ( rs2.next() ) {	//模組下要處理的檔案
			                	
			    	        	final boolean isMR = "t".equalsIgnoreCase(rs2.getString("isMR"));
			    	        	final long fileVer = rs2.getInt("fileVer");
			    	        	String relativePath = rs2.getString("relativePath");
			    	        	final String fileName = rs2.getString("fileName");
			    	        	System.out.println("現在要處理的檔案："+fileName);
			    	        	final String depFilePath = modId+"/"+relativePath+fileName;	//欲部署的檔案在那裡，此時的relativePath，其尾巴還是有斜線的
			    	        	//final String depFilePath = fileName;	//欲部署的檔案在那裡，此時的relativePath，其尾巴還是有斜線的
			    	        	String sourceFilePath = "";
			    	        	if ( isMR ) {
			    	        		apRepo = apMRrepo;
			    	        		sourceFilePath = wid.querySourceSVN(subsysId, modId).getS1()+"/"+relativePath+fileName;
			    	        	} else {
			    	        		apRepo = apOLrepo;
			    	        		sourceFilePath = wid.querySourceSVN(subsysId, modId).getS2()+"/"+relativePath+fileName;
			    	        	}
			    	        	//在SVN CM OL 的repo中，將相對路徑的夾都建立出來
			    	        	if ( relativePath.lastIndexOf("/") == relativePath.length()-1 && relativePath.length() !=0 ) {
			    	        		relativePath = relativePath.substring(0, relativePath.length()-1);
			    	        	}
			    	        	/*String tmpPath = "";
			    	        	
			    	        	if( relativePath.length() > modId.length()  ) {
			    	        		tmpPath = relativePath.substring(modId.length()+1, relativePath.length());
			    	        	}*/
			    	        	
			    	        	final String[] arrRP = relativePath.split("/");
			    	        	final int hierarchicalNumber = arrRP.length;
			    	        	String currentPath = modId;
			    	        	System.out.println("處理中的檔案："+depFilePath+",階層為"+hierarchicalNumber);
			    	        	
			    	        	for( int i=0; i<hierarchicalNumber;i++ ) {
			    	        		currentPath += "/"+arrRP[i];
			    	        		try {	//若能夠打開該路徑的資料夾，便是該夾存在，否則便進入catch來創建該資料夾
			    	        			System.out.println("嘗試新增："+currentPath);
			    		        		editor.addDir(arrRP[i],null,-1);
			    		        	} catch ( Exception e) {
			    		        		System.out.println("無法新增，嘗試打開"+currentPath);
			    		        		//editor.openDir(arrRP[i],-1);
			    		        	}
			    	        		
			    	        	}
			    	        	//editor.openDir(relativePath,-1);
			    	        	
			    	        	try {
			    	        		System.out.println("嘗試新增檔案"+fileName);
			    	        		editor.addFile( depFilePath,null,-1);
			    		        } catch(Exception e) {
			    		        	System.out.println("無法新增改成開起");
			    		        	editor.openFile( depFilePath,-1);
			    		        }
			    		        
			    	        	apRepo.getFile( sourceFilePath,fileVer, fileProperties, baos);
			    		        input  = new ByteArrayInputStream( baos.toByteArray() );
			    		        
			    	    		editor.applyTextDelta( depFilePath, null ); 
			    	    		String baseChecksum = deltaGenerator.sendDelta( depFilePath, input, editor, true);
			    	    	    editor.textDeltaEnd( depFilePath );
			    	    	    editor.closeFile( depFilePath, baseChecksum );
			    	    	    
			    	    	    for( int i=0; i<hierarchicalNumber;i++ ) {
			    	    	    	editor.closeDir();	
			    	    	    }
			    	    	    
			    	        }
			    	        editor.closeDir();			//關閉 modId
			    	        info =  editor.closeEdit();			//本次提交結束
			            }
			            
			            
				        //寫回 SVN CM的版次
			    	    final long cmOLrepoVer = info.getNewRevision();
			    	    sql = "UPDATE chkinmain SET cmFileVer=? WHERE chkInMainId=?";
			    	    ps = conn.prepareStatement(sql);
			    	    ps.setLong(1, cmOLrepoVer);
						ps.setInt(2, chkInId);
						ps.execute();
					} catch(Exception e) {
						wid.errLog(e);
						//簽入SVN CM 原始碼區失敗
						chkInStatusId = 17;
						wid.updateCHKINstatus(chkInId, chkInStatusId);
						throw e;
					}
					
					/**
					 *	計算此模組所推導的最後部署模組有那些
					 *	還有中間經過的點，以及上述各點的預期Build版的job版數 
					 **/
					//簽入CM 原始碼庫完成，開始計算部署相依性
					chkInStatusId = 4;
					wid.updateCHKINstatus(chkInId, chkInStatusId);
					
					sql = "SELECT modId FROM chkinmod WHERE chkInMainId=?";
					ps = conn.prepareStatement(sql);
					ps.setInt(1, chkInId);
					rs = ps.executeQuery();
					
					ArrayList<JobDepStruc> jds = new ArrayList<JobDepStruc>();	//儲存這次繳交有影響到的模組
					
					while ( rs.next() ) {
						//產生屬於該模組的樹結構
						JobDepStruc jd = new JobDepStruc( rs.getString("modId"),false,subsysId );
						jd = wid.buildTree(jd);
						jd.setUp(null);
						jds.add(jd);
					}
					/**	
					 *	比對模組間是否存在依附的關係，如  A模組在建置的過程中會產生B，最後推導出C.ear
					 *	這時 A 和 B 的內容都被提交，則最後僅該部署一次 c.ear即可
					 *
					 *	故以下程式的邏輯便是檢查，若 B tree 包含在 A tree中，之間刪除 B tree，僅執行 A tree的動作即可
					 */
					
					//	b1 是否被包含
					//	i1 流水號
					final ArrayList<B1I1> jds4Chk = new ArrayList<B1I1>();	//與所有節點同樣大小的清單
					for(int i=0;i<jds.size();i++) {	//初始化 jds4Chk， 其中的B1皆先設為false，假設沒有要跑
						final B1I1 jd4Chk = new B1I1();
						jd4Chk.setI1(i);
						jd4Chk.setB1(false);	//false代表沒有被包含，B1代表  isContained
						System.out.println("建立了"+jds.get(i).getModId()+"的核對ArrayList");
						jds4Chk.add(jd4Chk);
					}
					for(int i=0;i<jds.size();i++) {
						for(int j=0;j<jds.size();j++) {
							if ( i!=j && !jds4Chk.get(j).isB1() ) {	//不用比對自己，而且若自己已證實被包含，也不用算了
								if( jds.get(i).isContain(jds.get(i), jds.get(j).getModId()) ){
									System.out.println(jds.get(i).getModId()+"包含了"+jds.get(j).getModId());
									jds4Chk.get(j).setB1(true);
								} else {
									System.out.println(jds.get(i).getModId()+"未包含"+jds.get(j).getModId());
								}
							}
						}
					}
					//利用上面比對的結果，移除掉會被重覆部署的chain
					for(int i=0;i<jds4Chk.size();i++) {
						if( jds4Chk.get(i).isB1() ){
							System.out.println(jds.get(i).getModId()+"被包含了，要移除");
							jds.remove(i);
						}
					}
					
					/**
					 * 開始處理 Build版封裝
					 * */
					//準備驅動jenkins
					chkInStatusId = 5;
					wid.updateCHKINstatus(chkInId, chkInStatusId);
					
					for(int i=0;i<jds.size();i++) {
						/*
						 * 1. 將第一層的模組，其xml加上chkInId的參數
						 * 2. 驅動部署
						 * */
						final String jobName = (jds.get(i).isMR()?"MR":"OL")+"_"+jds.get(i).getSubsysId()+"_"+jds.get(i).getModId();
						final String upstreamJobName = jobName;
						final String rootJobName = jobName;
						wid.updateJenkinsJobPostBuildURL(jobName,chkInId,subsysId,applierId, upstreamJobName, 0, rootJobName,0);
						//wid.updateJobPostBuildURL( jobName, chkInId,null,0,null, 0, applierId);
						try {
							System.out.println("要觸發的Job是："+jobName);
							wid.triggerJenkinsBuild( jobName );
						} catch (Exception e) {
							//驅動jenkins失敗
							chkInStatusId = 6;
							wid.updateCHKINstatus(chkInId, chkInStatusId);
							wid.errLog(e);
						}
					}
				}
				
				//寄發通知信
				wid.sendMailMain(mailList, mailSubject, mailBody);
				
				/****完成這次的異動****/
				sql = "UPDATE changelog SET eDate=? WHERE changeLogId=?";
				ps = conn.prepareStatement(sql);
				ps.setTimestamp(1, wid.cDT());
				ps.setInt(2, chkInId4Approve);
				ps.execute();
				
				res="OK";
			}
		} catch(Exception e) {
			res = wid.errProcess(e);
			wid.errLog(e);
		} finally {
			try {
				if ( baos !=null ) {baos.close();}
				if ( input !=null ) {input.close();}
				if ( rs !=null ) {rs.close();}
				if ( rs2 !=null ) {rs2.close();}
				if ( ps !=null ) {ps.close();}
				if ( ps2 !=null ) {ps2.close();}
				if ( conn !=null ) {conn.close();}
				if ( cmOLrepo != null ) { cmOLrepo.closeSession(); }
				if ( cmOLrepoCHK != null ) { cmOLrepoCHK.closeSession(); }
				if ( apMRrepo != null ) { apMRrepo.closeSession(); }
				if ( apOLrepo != null ) { apOLrepo.closeSession(); }
				if ( apRepo != null ) { apRepo.closeSession(); }
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		response.getWriter().write(res);
	}
}